Add Build Flow view for better visualization of Upstream and Downstream Builds - #1312
Add Build Flow view for better visualization of Upstream and Downstream Builds#1312adityajalkhare wants to merge 13 commits into
Conversation
…ph-view-plugin into feature/issue-1310-e2e-build-flow
|
Does this detect upstream/downstream relationships solely from how builds are triggered, or does this also detect if a downstream build is triggered by something unrelated (e.g. cron) and then uses the copyArtifacts step to copy artifacts from an upstream build? I'm asking because we can get hundreds of downstream builds in the copyArtifacts case and I'm concerned that looking up all of those could be excessively slow. |
|
Thanks for calling this out. I checked the implementation to make sure before replying. This feature is trigger-based only, not artifact-based.
So if a build starts independently, for example from cron, and then uses So the graph is following explicit build-trigger relationships, not artifact-consumption relationships. On the performance concern, this code is not scanning all builds looking for artifact usage. It reads trigger metadata from the current build chain, and it also has hard safety caps:
So the case of getting hundreds of downstream builds purely because of That said, I do not have an exact or near-replica of your pipeline setup locally, so if you have a representative case handy, please feel free to test it. If it behaves differently in practice, I can follow up and revert or adjust the implementation as needed. |
- Extract all inline SVGs into BuildFlowIcons.tsx with a reusable ToggleButton component - Deduplicate element-ID lookup via shared getRootElement() export - Unify statusColor/resultDotColor into a single STATUS_COLORS map - Extract shared SCSS into _card-heading.scss and _card-controls.scss mixins, replacing verbatim copies in BuildFlow.scss, stages.scss, and pipeline-graph-view/app.scss - Add status-stripe mixin and $icon-inline-offset variable to reduce repeated SCSS patterns - Visual polish: status-colored left border with tinted background, node entry animation, edge fade-in, focus-visible ring, shared StatusIcon integration - Add BuildFlowScss.spec.ts and FocusPath.spec.ts
|
Looks awesome! 😍 |
|
@novinxy - yes. I used that only. |
…c, and adjust edge animation timing on hover for a better UX
…m/adityajalkhare/pipeline-graph-view-plugin into feature/issue-1310-e2e-build-flow
|
I haven’t looked in detail yet and will need more time and ideally feedback from others
|
|
I'm not sure if the others need fading out but yes some sort of border to show 'active' or 'selected' would be better |
|
The fade out was not on select, it was on hover... I just took a screenshot at the wrong time :) |
…id accent color like Jenkins does
|
For me fade out looks clear and makes it easy to see selected "flow" Those builds on each card are stages from the pipelines ? Edit: Nvm, description answered that for me. Those are recent builds |
|
This flow shows pipeline relations, but is it easy to analyze individual flow of the builds? I mean such situation:
If not then I think simple "active border" on each related run would be sufficient :) |
The build flow does not load in this case for the latest build where A failed before triggering B.
If A fails after triggering B, which triggered C, and if _We can also test these scenarios for better understanding. _
Hover is intended for a quick glance of the flow between job nodes (one parent and one child), while being anywhere in the graph, irrespective of the selected/active job.
The though process, and what I have seen is - Bezier curves/Connector lines are the tracking mechanisms users use to track the flow. I would prefer not fading the whole unrelated flow based on selected job, unless there is a strong reasoning since then the "here's the whole Build Flow" core idea and glancing through functionality would break. When I think of it..... There is an hide/show upstream/downstream toggle, perhaps there can be another one, "View Active Job Flow/name something else" which can let us view what you suggested. @novinxy WDYT about this?
I'm also just starting out, so no idea lol... ;) |
|
@timja @novinxy Also updated it in description |
There was a problem hiding this comment.
Pull request overview
Adds a new Build Flow view that visualizes upstream/downstream build-trigger relationships as an interactive, zoomable DAG. Introduces a new frontend Vite entry, React component with focus-path highlighting and auto-refresh, a backend traversal/DTO package with API endpoints, transient action factories for both Run and Job, and supporting Jelly views for embedded card, sidebar, and full-page contexts.
Changes:
- New backend
buildflowpackage (action + job action + traversal + DTOs) withbuild-flow/apiendpoint and a transient-action reentrance guard forRun-level registration. - New frontend
build-flow-viewapp (DAG layout, formatting utilities, focus path, icons, SCSS) plus shared_card-heading.scss/_card-controls.scssmixins extracted from the Stages graph. - Backend (
@WithJenkins) and frontend (Vitest) test suites covering traversal, layout, focus paths, SCSS tokens, and status mapping; new i18n keys (buildFlow.*).
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
vite.config.ts |
Adds build-flow-view Vite entry point |
README.md |
Documents the new Build Flow feature |
src/main/resources/.../Messages.properties |
Adds buildFlow.* i18n keys |
src/main/frontend/common/i18n/messages.ts |
Adds LocalizedMessageKey.buildFlow* and defaults |
src/main/frontend/common/styles/_card-heading.scss |
New shared mixin extracted from Stages card |
src/main/frontend/common/styles/_card-controls.scss |
New shared button/positions mixins |
src/main/frontend/pipeline-console-view/.../stages.scss |
Switches to shared heading mixin |
src/main/frontend/pipeline-graph-view/app.scss |
Switches to shared controls mixin |
src/main/frontend/build-flow-view/index.tsx |
App bootstrap |
src/main/frontend/build-flow-view/app.tsx |
i18n providers and BuildFlow mount |
src/main/frontend/build-flow-view/build-flow/main/BuildFlow.tsx |
Main React component (graph, controls, polling, focus) |
src/main/frontend/build-flow-view/build-flow/main/BuildFlow.scss |
Styles, animations, a11y media queries |
src/main/frontend/build-flow-view/build-flow/main/BuildFlowIcons.tsx |
Inline SVG icons and ToggleButton helper |
src/main/frontend/build-flow-view/build-flow/main/BuildFlowLayout.ts |
Pure DAG/flat layout + formatting |
src/main/frontend/build-flow-view/build-flow/main/BuildFlowUtils.ts |
DOM/dataset helpers, status mapping, computeFullPath |
src/main/frontend/build-flow-view/build-flow/main/*.spec.ts |
Vitest coverage for utils/layout/focus/scss |
src/main/frontend/build-flow-view/build-flow/model/BuildFlowModel.ts |
Frontend DTO types |
src/main/java/.../cards/items/BuildFlowRunDetailsItem.java |
Details-bar item (not wired into aggregator) |
src/main/java/.../buildflow/BuildFlowAction.java |
Run-page Tab with api endpoint |
src/main/java/.../buildflow/BuildFlowActionFactory.java |
Run transient action factory with reentrance guard |
src/main/java/.../buildflow/BuildFlowJobAction.java |
Job-page action with api endpoint |
src/main/java/.../buildflow/BuildFlowJobActionFactory.java |
Job transient action factory |
src/main/java/.../buildflow/BuildFlowGraph.java |
Traversal + DAG assembly + status mapping |
src/main/java/.../buildflow/BuildFlowNode.java / BuildFlowEdge.java / BuildFlowResponse.java |
Backend DTO records and JSON envelope writer |
src/main/resources/.../buildflow/BuildFlowAction/*.jelly |
action, index, summary, widget views |
src/main/resources/.../buildflow/BuildFlowJobAction/*.jelly |
index (full page) and jobMain (embedded card) |
src/test/java/.../buildflow/BuildFlowActionTest.java |
API + action registration tests |
src/test/java/.../buildflow/BuildFlowGraphTest.java |
Traversal/edge-case integration tests |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public class BuildFlowRunDetailsItem { | ||
|
|
||
| public static Optional<RunDetailsItem> get(Run<?, ?> run) { | ||
| if (!BuildFlowGraph.hasUpstreamOrDownstream(run)) { | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| RunDetailsItem item = new RunDetailsItem.RunDetail( | ||
| new Ionicon("git-network-outline"), ItemContent.of("build-flow", "Build Flow"), "View build chain"); | ||
| return Optional.of(item); | ||
| } | ||
| } |
| @Override | ||
| public String getDisplayName() { | ||
| return "Build Flow"; | ||
| } |
| </j:when> | ||
| <j:otherwise> | ||
| <div class="jenkins-!-margin-top-2"> | ||
| <p>No builds yet.</p> |
| <ToggleButton | ||
| icon={IconChevronUp} | ||
| label="" | ||
| active={showUpstream} | ||
| onToggle={() => setShowUpstream(!showUpstream)} | ||
| tooltip={showUpstream ? "Hide upstream" : "Show upstream"} | ||
| /> | ||
| <ToggleButton | ||
| icon={IconChevronDown} | ||
| label="" | ||
| active={showDownstream} | ||
| onToggle={() => setShowDownstream(!showDownstream)} | ||
| tooltip={showDownstream ? "Hide downstream" : "Show downstream"} | ||
| /> | ||
| <ToggleButton | ||
| icon={IconLocate} | ||
| label="" | ||
| active={focusCurrentFlow} | ||
| onToggle={() => setFocusCurrentFlow(!focusCurrentFlow)} | ||
| tooltip={ | ||
| focusCurrentFlow ? "Show full graph" : "Focus current build's flow" | ||
| } | ||
| /> | ||
| <ToggleButton | ||
| icon={IconBarChart} | ||
| label="" | ||
| active={showBuildHistory} | ||
| onToggle={() => setShowBuildHistory(!showBuildHistory)} | ||
| tooltip={showBuildHistory ? "Hide build history" : "Show build history"} | ||
| /> | ||
| <ToggleButton | ||
| icon={IconRefreshCircle} | ||
| label="" | ||
| active={autoRefresh} | ||
| onToggle={() => setAutoRefresh(!autoRefresh)} | ||
| tooltip={autoRefresh ? "Disable auto-refresh" : "Enable auto-refresh"} | ||
| /> | ||
| </> | ||
| ); | ||
| }; |
| function computeHighlightedIds( | ||
| hoveredNodeId: string | null, | ||
| edges: BuildFlowEdgeModel[], | ||
| flattenGraph: boolean, | ||
| ): Set<string> | null { | ||
| if (!hoveredNodeId || flattenGraph) return null; | ||
| const set = new Set<string>([hoveredNodeId]); | ||
| for (const edge of edges) { | ||
| if (edge.from === hoveredNodeId) set.add(edge.to); | ||
| if (edge.to === hoveredNodeId) set.add(edge.from); | ||
| } | ||
| return set; | ||
| } |
| @Test | ||
| void recentResults_returnsUpToFiveHistoryItems() throws Exception { | ||
| // Create the job once, then run it 3 times | ||
| org.jenkinsci.plugins.workflow.job.WorkflowJob job = | ||
| TestUtils.createJob(j, "multi-run", "helloWorldScriptedPipeline.jenkinsfile"); | ||
| j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); | ||
| j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); | ||
| j.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); | ||
|
|
||
| WorkflowRun lastRun = job.getLastBuild(); | ||
|
|
||
| BuildFlowGraph graph = new BuildFlowGraph(lastRun, true, true); | ||
| BuildFlowResponse response = graph.build(); | ||
|
|
||
| BuildFlowNode node = response.nodes().get(0); | ||
| assertThat(node.recentResults(), hasSize(3)); // current + 2 previous builds | ||
| assertThat(node.recentResults(), everyItem(is("SUCCESS"))); | ||
| } |
timja
left a comment
There was a problem hiding this comment.
looking really good, I've had a reasonable look over although there's a lot of code.
few comments for improvements
| * Registers {@link BuildFlowJobAction} on every {@link Job}. | ||
| */ | ||
| @Extension | ||
| public class BuildFlowJobActionFactory extends TransientActionFactory<Job> { |
There was a problem hiding this comment.
do we need this on a job?
Watching it while running it was surprising that it rebuilt the flow for the running build i.e. the last flow disappeared and as new jobs started they appeared in the flow.
as currently built it seems more build specific, you can always turn on the previous build options when viewing it to see how its been running over time.
There was a problem hiding this comment.
I added this on the job page since that is where a user "lands" first and can easily look through the flow for the latest build, finished or currently running, and browse upstream/downstream without going on the build page (for the latest build at least).
This behavior is the same as how it is in Yet Another Build Visualizer.
From as user perspective, I would like it there.
From the perspective we are discussing it, I suggest we give users a toggle in Global Settings to Show/Hide Build Flow like we have for Stage Names and Duration.
WDYT?
There was a problem hiding this comment.
I have added a toggle in Global Settings which ADMINS can use to enable/disable the Build Flow on Job Page.
For a USER specific toggle, I am thinking where to place it.
|
|
||
| Hidden steps are not displayed by default in the Pipeline Overview, but can be toggled visible using the filter controls. | ||
|
|
||
| ## Build Flow |
There was a problem hiding this comment.
can you move this after the screenshots section and include a screenshot of this too
| if (run.isBuilding()) { | ||
| return "IN_PROGRESS"; | ||
| } | ||
| Result result = run.getResult(); | ||
| if (result == null) { | ||
| return "IN_PROGRESS"; | ||
| } | ||
| if (result.equals(Result.SUCCESS)) { | ||
| return "SUCCESS"; | ||
| } | ||
| if (result.equals(Result.FAILURE)) { | ||
| return "FAILURE"; | ||
| } | ||
| if (result.equals(Result.UNSTABLE)) { | ||
| return "UNSTABLE"; | ||
| } | ||
| if (result.equals(Result.ABORTED)) { | ||
| return "ABORTED"; | ||
| } | ||
| return "NOT_BUILT"; |
There was a problem hiding this comment.
This is simpler, (If we bump Jenkins version we could move to java 21 to simplify further)
| if (run.isBuilding()) { | |
| return "IN_PROGRESS"; | |
| } | |
| Result result = run.getResult(); | |
| if (result == null) { | |
| return "IN_PROGRESS"; | |
| } | |
| if (result.equals(Result.SUCCESS)) { | |
| return "SUCCESS"; | |
| } | |
| if (result.equals(Result.FAILURE)) { | |
| return "FAILURE"; | |
| } | |
| if (result.equals(Result.UNSTABLE)) { | |
| return "UNSTABLE"; | |
| } | |
| if (result.equals(Result.ABORTED)) { | |
| return "ABORTED"; | |
| } | |
| return "NOT_BUILT"; | |
| if (run.isBuilding()) { | |
| return "IN_PROGRESS"; | |
| } | |
| Result result = run.getResult(); | |
| if (result == null) { | |
| return "IN_PROGRESS"; | |
| } | |
| return switch (result.toString()) { | |
| case "SUCCESS", "FAILURE", "UNSTABLE", "ABORTED" -> result.toString(); | |
| default -> "NOT_BUILT"; | |
| }; |
There was a problem hiding this comment.
So we move to baseline Jenkins 2.555.3 then?
| List<String> results = new ArrayList<>(6); | ||
| // Include the current build's result as the most recent entry | ||
| results.add(mapStatus(run)); | ||
| Run<?, ?> previous = run.getPreviousBuild(); |
There was a problem hiding this comment.
We should avoid loading previous builds into memory if they aren't already there.
This is how JUnit does it:
|
|
||
| // --- Preferences persistence --- | ||
|
|
||
| interface BuildFlowPrefs { |
There was a problem hiding this comment.
don't shorten names, applies to all occurrences:
| interface BuildFlowPrefs { | |
| interface BuildFlowPreferences { |
| @@ -0,0 +1,957 @@ | |||
| import "./BuildFlow.scss"; | |||
There was a problem hiding this comment.
Can you refactor this file to split some sections of it out logically, e.g. components or grouped functions
its a large file as-is
| </svg> | ||
| ); | ||
|
|
||
| export const IconClose = ( |
There was a problem hiding this comment.
an example of what should be in a different file, isn't there an existing close icon you can reuse though?
| <?jelly escape-by-default='true'?> | ||
| <j:jelly xmlns:j="jelly:core"> | ||
| <j:if test="${it.shouldDisplayBuildFlow()}"> | ||
| <div class="jenkins-card" |
There was a problem hiding this comment.
There was a problem hiding this comment.
I thought and propose renaming the Stages widget to Flows, keeping the same icon for a wider coverage of both Stage Flows and Build Flows, WDYT?
There was a problem hiding this comment.
I have removed the Build Flow Tab and integrated the jelly for it with PipelineConsoleViewAction/widget.jelly.
However, it will render the overview page as follows -

To actually move Build Flow below Console, we'd need to either make BuildFlowAction a Tab action again so its widget renders on the Overview without disturbing Stages and Console.
My proposal to rename the Stages Tab as Flows or something else that can be discussed still stands.
There was a problem hiding this comment.
my suggestion was to combine them into one widget and allow switching between them.
i.e. Add a build flow button to the stages one
… Flow Integration - Refactored MainViewVisibility enum to include GRAPH_AND_STAGES, ALL, and BUILD_FLOW_ONLY options. - Added new styles for Build Flow card in pipeline-console.scss. - Enhanced user-preference-provider to manage build flow height and collapsed state. - Improved SplitView component to support collapsible build flow panel. - Updated PipelineGraphViewConfiguration to include a setting for showing Build Flow on job pages. - Refactored BuildFlowAction to provide API endpoint without a separate tab. - Removed obsolete BuildFlowRunDetailsItem class. - Updated console view actions to integrate Build Flow display logic. - Added tests to ensure recent results cap is correctly implemented.
|
@adityajalkhare ant plan to continue? |
|
@panicking yeah. I'll pick it up after 15th July and see if I can add it to YABV's own repo if it doesn't take a lot of time. Last I saw it had lot's of upgrade issues open. If not I'll see if creating a new plugin in itself is a better idea. But the Build Flow won't be a part of Pipeline Graph View as others suggested above. |
I think the flow should go here, I don't see any reason why this should not happen. There use case where the jobs are composed by multiple pipeline? Why the flow as a tab is not welcome? |
|
@panicking, @janfaracik and @timja have strongly suggested to keep this flow in the OG plugin itself in #1310 |









Closes #1310
Summary
Adds a Build Flow view that renders upstream and downstream build-trigger relationships as an interactive, zoomable directed graph. Uses the shared Jenkins Design Library status icons, Tippy.js dropdowns, and react-zoom-pan-pinch for pan/zoom.
Screenshots/Demo
Over-Complicated Job
Screenshot
Live Video
Build.Flow.Demo.-.Pipeline.Graph.Plugin.mov
Focused View of a Job
Focused.View.of.a.Job.-.Build.Flow.mov
Simple Job
Build Flow Tab
Overview Tab
Job Page
Features
Where it appears
Detection method
Trigger-based only. Uses
Cause.UpstreamCauseandDownstreamBuildAction. Does not scan artifacts or copyArtifacts relationships.Implementation
BuildFlow.tsx,BuildFlowLayout.ts,BuildFlowUtils.ts,BuildFlow.scssBuildFlowModel.ts,BuildFlowNode.java,BuildFlowEdge.javaBuildFlowGraph.java,BuildFlowResponse.javaBuildFlowAction.java,BuildFlowJobAction.java+ factoriesbuildFlow/graphTesting
mvn hpi:runon sequential, parallel, nested-stage, and multi-branch pipelines